#!/usr/bin/env python2
import codecs
import serial
import socket
import sys
import threading

Gcommands = {
    'previous': '',
    'buffer': [],
}

class Redirector:
    def __init__(self, serial_instance, socket, spy=False):
        self.serial = serial_instance
        self.socket = socket
        self.spy = spy
        self._write_lock = threading.Lock()

    def shortcut(self):
        """connect the serial port to the TCP port by copying everything
           from one side to the other"""
        self.alive = True
        self.thread_read = threading.Thread(target=self.reader)
        self.thread_read.setDaemon(True)
        self.thread_read.setName('serial->socket')
        self.thread_read.start()
        self.writer()

    def reader(self):
        """loop forever and copy serial->socket"""
        while self.alive:
            try:
                data = self.serial.read(1)              # read one, blocking
                n = self.serial.inWaiting()             # look if there is more
                if n:
                    data = data + self.serial.read(n)   # and get as much as possible
                if data:
                    # the spy shows what's on the serial port, so log it before converting newlines
                    if self.spy:
                        sys.stdout.write(codecs.escape_encode(data)[0])
                        sys.stdout.flush()
                    self.write(data)
            except socket.error, msg:
                sys.stderr.write('\nreader ERROR: %s\n' % msg)
                # probably got disconnected
                break
        self.alive = False

    def write(self, data):
        """thread safe socket write"""
        self._write_lock.acquire()
        try:
            self.socket.sendall(data)
        finally:
            self._write_lock.release()

    def writer(self):
        """loop forever and copy socket->serial"""
        data = ''
        while self.alive:
            try:
                #print "Recv..."
                while '\n' not in data:
                    print "Recving, now in "+repr(data)
                    newdata = self.socket.recv(1024)
                    if not newdata:
                        print "Connection closed, oh noes!"
                        self.socket.close()
                        break
                    data += newdata
                commands = data.split('\n')
                data = commands.pop()
                for command in commands:
                    print "Command: "+repr(command)
                    if command == 'quit':
                        self.socket.close()
                        self.alive = False
                        #print "***BREAKING***"
                        break
                    if command == 's' and Gcommands['previous'] == 's':
                        break
                    self.serial.write(command)
                    self.serial.write('\n')
                    Gcommands['previous'] = command
                    if self.spy:
                        sys.stdout.write(codecs.escape_encode(command)[0]+'\n')
                        sys.stdout.flush()
            except socket.error, msg:
                sys.stderr.write('\nwriter ERROR: %s\n' % msg)
                # probably got disconnected
                break
        self.alive = False
        self.serial.write('s\n')
        self.thread_read.join()

    def stop(self):
        """Stop copying"""
        if self.alive:
            self.alive = False
            self.thread_read.join()



socl = socket.socket()
socl.bind(('', 7000))
socl.listen(2)

try:
    while True:
        print "Waiting..."
        soca, aadr = socl.accept()
        print "Got! From "+repr(aadr)
        ser = serial.Serial('/dev/ttyAMA0')
        try:
            r = Redirector(ser, soca, spy=False)
            r.shortcut()
            print "\nDone."
        finally:
            print "Closing accepted socket"
            soca.close()
            ser.close()
except KeyboardInterrupt:
    print "Ctrl+C pressed."
finally:
    print "Closing listening socket."
    socl.close()

